package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.to.mq.SecKillOrderTo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.order.constant.OrderConstant;
import com.atguigu.gulimall.order.dao.OrderItemDao;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.entity.PaymentInfoEntity;
import com.atguigu.gulimall.order.enume.OrderStatusEnum;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareFeignService;
import com.atguigu.gulimall.order.interceptor.LoginUserInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.service.PaymentInfoService;
import com.atguigu.gulimall.order.to.FareTo;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;

import org.aspectj.weaver.ast.Or;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    private ThreadLocal<OrderSubmitVo>  orderSubmitVothreadLocal=new ThreadLocal<>();
    @Autowired
    MemberFeignService memberFeignService;
    @Autowired
    CartFeignService cartFeignService;
    @Autowired
    ThreadPoolExecutor executor;
    @Autowired
    WareFeignService wareFeignService;
    @Autowired
    ProductFeignService productFeignService;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    OrderItemService orderItemService;
    @Autowired
    PaymentInfoService paymentInfoService;
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<OrderEntity> wrapper = new QueryWrapper<>();
        String key = (String)params.get("key");
        if (!StringUtils.isEmpty(key)){
            wrapper.and((wp)->{
                wp.eq("order_sn", key).or().like("member_username", key);
            });
        }
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    /**
     * 订单确认后需要返回的数据
     * @return
     */
    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        // 获取用户，用用户信息获取购物车
        UserInfo1 userInfo1 = LoginUserInterceptor.threadLocal.get();
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        System.out.println("主线程。。。"+Thread.currentThread().getId());

        // 从主线程获取用户数据 放到局部变量中
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

        //1.远程查询所有收货地址列表
        CompletableFuture<Void> getAdressFuture = CompletableFuture.runAsync(() -> {
            // 把旧RequestAttributes放到新线程的RequestContextHolder中
            RequestContextHolder.setRequestAttributes(attributes);
            System.out.println("子线程。。。"+Thread.currentThread().getId());
            List<MemberAddressVo> address = memberFeignService.getAddress(userInfo1.getUserId());
            confirmVo.setAddress(address);
        }, executor);
        CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
            //2.远程查询购物车所有选中的购物项
            //feign调用之前将调用拦截器
            // 把旧RequestAttributes放到新线程的RequestContextHolder中
            RequestContextHolder.setRequestAttributes(attributes);
            List<OrderItemVo> items = cartFeignService.getCurrentUserCartItems();
            confirmVo.setItems(items);
        }, executor).thenRunAsync(()->{
            List<OrderItemVo> items = confirmVo.getItems();
            List<Long> collect = items.stream().map(item -> {
                return item.getSkuId();
            }).collect(Collectors.toList());
            R hasStock = wareFeignService.getSkuHasStock(collect);
            List<SkuStockVo> data = hasStock.getData(new TypeReference<List<SkuStockVo>>() {
            });
            if(data!=null){
                Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
                confirmVo.setStocks(map);
            }

        },executor);

        //3.查询用户的积分
        Integer a=10;
        confirmVo.setIntegration(a);

        //4.其他数据自动计算
        //5.todo 防重令牌
        //服务器储存一份
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+userInfo1.getUserId(),
                token,30, TimeUnit.MINUTES);
        //页面保存一份，即用户
        confirmVo.setOrderToken(token);
        CompletableFuture.allOf(getAdressFuture,cartFuture).get();
        return confirmVo;
    }

    /**
     * 下单的实现
     * @param vo
     * @return
     * mysql默认的事务隔离级别(可重复读)(isolation = Isolation.REPEATABLE_READ)
     * serializable 串行执行，（innodb 给读操作隐式的加一把读锁）不出出现任何错误
     * 事务使用代理对象来控制
     * 同一个对象内事务方法互相调用默认失效，原因绕过了代理对象
     *
     */
//    @GlobalTransactional
    @Transactional  //在分布式的情况下本地的事务控制不了远程的服务回滚 最大的原因：网络问题
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {
        orderSubmitVothreadLocal.set(vo);
        //下单 ---创建订单，检验令牌，验金额，锁库存->1.成功到支付选择页--2.失败再回到订单确认页
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        // 获取用户，用用户信息获取购物车
        UserInfo1 userInfo1 = LoginUserInterceptor.threadLocal.get();
        responseVo.setCode(0);
        //1.验证令牌(对比和删除必须要是原子性的) 0：校验失败 1：成功
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then\n" +
                "return redis.call('del',KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        String orderToken = vo.getOrderToken();
        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), // 脚本和返回类型
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + userInfo1.getUserId()), // 参数
                orderToken);
        if (result==0L){
            //失败
            responseVo.setCode(1);
            return responseVo;
        }else {
            // 1.验证成功 创建订单等信息
            OrderCreateTo order = createOrder();
            // 2. 验价
            BigDecimal payAmount = order.getOrder().getPayAmount(); //后台计算的金额
            BigDecimal payPrice = vo.getPayPrice();//页面提交的金额
            if (Math.abs(payAmount.subtract(payPrice).doubleValue()) < 0.01) {
                //金额对比

                // todo 3.保存订单
                saveOrder(order);

                //4.锁定库存 (只要有异常就回滚订单数据)
                WareSkuLockVo lockVo = new WareSkuLockVo();
                lockVo.setOrderSn(order.getOrder().getOrderSn());
                List<OrderItemVo> orderItemVos = order.getOrderItems().stream().map(item -> {
                    OrderItemVo orderItemVo = new OrderItemVo();
                    orderItemVo.setSkuId(item.getSkuId());
                    orderItemVo.setCount(item.getSkuQuantity());
                    orderItemVo.setTitle(item.getSkuName());
                    return orderItemVo;
                }).collect(Collectors.toList());
                lockVo.setLocks(orderItemVos);
                //todo 4 远程调用锁定库存
                //库存成功了，但是网络原因超时了，订单回滚，库存不滚

                //为了保证高并发，库存服务自已回滚，可以发消息给库存服务
                //库存服务也可以使用自动解锁服务 -->消息队列
                R r = wareFeignService.orderLockStock(lockVo);
                if (r.getCode()==0){
                    //锁定库存成功
                    responseVo.setOrder(order.getOrder());
                    //todo 5.远程扣减积分 出异常(模拟)
//                    int a=5/0;  //订单回滚，库存不滚
                    //todo 订单创建成功，发送消息
                    rabbitTemplate.convertAndSend("order-event-exchange",
                            "order.create.order",order.getOrder());
                    return responseVo;
                }else {
                    //锁定库存失败 :3
//                    responseVo.setCode(3);
//                    return responseVo;
                    String msg=(String)r.get("msg");
                    throw  new NoStockException(msg);
                }

            }else {
                //验价失败
                responseVo.setCode(2);
                return responseVo;
            }

        }

    }
    //根据订单号查询订单
    @Override
    public OrderEntity getOrderByOrderSn(String orderSn) {
        OrderEntity order_sn = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return order_sn;
    }


    /**
     *  1.做好消息的确认机制(publisher,consumer[手动ack])
     *  2.每一个发送的消息都在数据库做好记录，失败的消息再次发送
     *  关闭订单
     * @param orderEntity
     */
    @Override
    public void closeOrder(OrderEntity orderEntity) {
        OrderEntity orderEntity1 = this.getById(orderEntity.getId());
        //什么情况下应该关闭订单
        //如果订单还处于新创建的状态，说明超时未支付，进行关单
        if (orderEntity1.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()) {
            //关单
            OrderEntity updateOrder = new OrderEntity();
            updateOrder.setId(orderEntity.getId());
            updateOrder.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(updateOrder);

            //关单后发送消息通知其他服务进行关单相关的操作，如解锁库存
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(orderEntity1,orderTo);

            rabbitTemplate.convertAndSend("order-event-exchange", "order.release.other",orderTo);
        }
    }



    /**
     * 获取当前订单的支付信息
     * @param orderSn
     * @return
     */
    @Override
    public PayVo getOrderPay(String orderSn) {
        PayVo payVo = new PayVo();
        OrderEntity orderEntity = this.getOrderByOrderSn(orderSn);
        //交易号
        payVo.setOut_trade_no(orderSn);
        //支付金额设置为两位小数，否则会报错
        BigDecimal payAmount = orderEntity.getPayAmount().setScale(2, BigDecimal.ROUND_UP);
        payVo.setTotal_amount(payAmount.toString());

        List<OrderItemEntity> orderItemEntities = orderItemService.list
                (new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
        OrderItemEntity orderItemEntity = orderItemEntities.get(0);
        //订单名称
        payVo.setSubject(orderItemEntity.getSkuName());
        //商品描述
        payVo.setBody(orderItemEntity.getSkuAttrsVals());

        return payVo;
    }

    /**
     * 查询下单成功后订单
     * @param params
     * @return
     */
    @Override
    public PageUtils getMemberOrderPage(Map<String, Object> params) {
        // 获取用户，用用户信息获取购物车
        UserInfo1 userInfo1 = LoginUserInterceptor.threadLocal.get();
        List<OrderEntity> list = this.list(new QueryWrapper<OrderEntity>().eq("member_id", userInfo1.getUserId()).orderByDesc("id"));
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id", userInfo1.getUserId()).orderByDesc("id")
        );
        //当前page中的数据
        List<OrderEntity> records = page.getRecords();
        List<OrderEntity> order_sn1 = records.stream().map(item -> {
            List<OrderItemEntity> itemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", item.getOrderSn()));
            item.setOrderItemEntities(itemEntities);
            return item;
        }).collect(Collectors.toList());
        page.setRecords(order_sn1);

        return new PageUtils(page);
    }

    /**
     * 处理支付宝的返回数据
     * @param payAsyncVo
     * @return
     */
    @Override
    public String handlerPayResult(PayAsyncVo payAsyncVo) {
        //保存交易流水
        PaymentInfoEntity infoEntity = new PaymentInfoEntity();
        String orderSn = payAsyncVo.getOut_trade_no(); //对外交易订单就是我们的订单号
        infoEntity.setOrderSn(orderSn);
        //流水号
        infoEntity.setAlipayTradeNo(payAsyncVo.getTrade_no());
        infoEntity.setSubject(payAsyncVo.getSubject());
        String trade_status = payAsyncVo.getTrade_status();
        infoEntity.setPaymentStatus(trade_status);
        infoEntity.setCreateTime(new Date());
        infoEntity.setCallbackTime(payAsyncVo.getNotify_time());
        paymentInfoService.save(infoEntity);

        //修改订单的状态信息
        //判断交易状态是否成功
        if (trade_status.equals("TRADE_SUCCESS") || trade_status.equals("TRADE_FINISHED")) {
            //支付成功
            this.baseMapper.updateOrderStatus(orderSn, OrderStatusEnum.PAYED.getCode());
        }


        return "success";
    }

    @Override
    public void createSeckillOrder(SecKillOrderTo orderTo) {
        //1. 创建订单
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderTo.getOrderSn());
        orderEntity.setMemberId(orderTo.getMemberId());
        orderEntity.setMemberUsername(orderTo.getMemberName());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setCreateTime(new Date());
        orderEntity.setPayAmount(orderTo.getSeckillPrice().multiply(new BigDecimal(orderTo.getNum())));
        this.save(orderEntity);

        //保存订单项信息
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(orderTo.getOrderSn());
        orderItemEntity.setSkuQuantity(orderTo.getNum());
        orderItemService.save(orderItemEntity);

    }


    /**
     * 保存订单数据
     * @param order
     */
    private void saveOrder(OrderCreateTo order) {
        //订单的详情
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setCreateTime(new Date());
        orderEntity.setModifyTime(new Date());
        this.save(orderEntity);
        //保存订单号
        String orderSn = order.getOrder().getOrderSn();
        OrderEntity orderEntity1 = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        //订单的商品的详情项
        List<OrderItemEntity> orderItems = order.getOrderItems();
        List<OrderItemEntity> collect = orderItems.stream().map(item -> {
            item.setOrderId(orderEntity1.getId());
            return item;
        }).collect(Collectors.toList());
        orderItemService.saveBatch(collect);

    }

    //创建订单的方法
    private OrderCreateTo createOrder(){
        OrderCreateTo orderTo = new OrderCreateTo();
        //生成订单号
        String orderSn = IdWorker.getTimeId(); //时间 ID = Time + ID,可用于商品的订单
        //1.创建订单
        OrderEntity orderEntity = buildOrder(orderSn);

        //2.获取到所有的订单项：即这个订单有哪些商品
        List<OrderItemEntity> itemEntities = buildOrderItems(orderSn);

        // 3.根据订单项计算价格	传入订单 、订单项 计算价格、积分、成长值等相关信息
        computerPrice(orderEntity, itemEntities);
        orderTo.setOrder(orderEntity);
        orderTo.setOrderItems(itemEntities);
        return orderTo;
    }

    private void computerPrice(OrderEntity orderEntity, List<OrderItemEntity> itemEntities) {
        BigDecimal totalPrice = new BigDecimal("0.0");
        // 叠加每一个订单项的金额
        BigDecimal coupon = new BigDecimal("0.0"); //优惠券金额
        BigDecimal integration = new BigDecimal("0.0"); //积分总额
        BigDecimal promotion = new BigDecimal("0.0"); //促销打折
        BigDecimal gift = new BigDecimal("0.0"); //赠送积分
        BigDecimal growth = new BigDecimal("0.0"); //成长值

        for (OrderItemEntity item:itemEntities){
            // 优惠券的金额
            coupon = coupon.add(item.getCouponAmount());
            // 积分优惠的金额
            integration = integration.add(item.getIntegrationAmount());
            // 打折的金额
            promotion = promotion.add(item.getPromotionAmount());
            BigDecimal realAmount = item.getRealAmount();
            totalPrice = totalPrice.add(realAmount);

            // 购物获取的积分、成长值
            gift.add(new BigDecimal(item.getGiftIntegration().toString()));
            growth.add(new BigDecimal(item.getGiftGrowth().toString()));

        }
        // 1.订单价格相关 总额、应付总额
        orderEntity.setTotalAmount(totalPrice);//总额
        orderEntity.setPayAmount(totalPrice.add(orderEntity.getFreightAmount()));//应付总额

        orderEntity.setPromotionAmount(promotion);
        orderEntity.setIntegrationAmount(integration);
        orderEntity.setCouponAmount(coupon);

        // 设置积分、成长值
        orderEntity.setIntegration(gift.intValue());
        orderEntity.setGrowth(growth.intValue());

        // 设置订单的删除状态
        orderEntity.setDeleteStatus(OrderStatusEnum.CREATE_NEW.getCode());
    }

    //生成订单
    private OrderEntity buildOrder(String timeId) {
        UserInfo1 userInfo1 = LoginUserInterceptor.threadLocal.get();
        userInfo1.setUserId(userInfo1.getUserId());
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(timeId);

        //1) 设置用户信息
        orderEntity.setMemberId(userInfo1.getUserId());
        orderEntity.setMemberUsername(userInfo1.getUserName());
        //2) 获取邮费和收件人信息并设置
        OrderSubmitVo orderSubmitVo = orderSubmitVothreadLocal.get();
        FareTo fare = wareFeignService.getFare(orderSubmitVo.getAddrId());
        orderEntity.setFreightAmount(fare.getFare());
        orderEntity.setReceiverCity(fare.getAddress().getCity());
        orderEntity.setReceiverDetailAddress(fare.getAddress().getDetailAddress());
        orderEntity.setReceiverName(fare.getAddress().getName());
        orderEntity.setReceiverPhone(fare.getAddress().getPhone());
        orderEntity.setReceiverPostCode(fare.getAddress().getPostCode());
        orderEntity.setReceiverProvince(fare.getAddress().getProvince());
        orderEntity.setReceiverRegion(fare.getAddress().getRegion());

        //3) 设置订单相关的状态信息
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setConfirmStatus(0);
        orderEntity.setAutoConfirmDay(7);
        return orderEntity;
    }

    /**
     * 构建订单项的所有数据
     * @return
     */
    private List<OrderItemEntity> buildOrderItems(String orderSn) {
        //1.最后确定每个购物项的价格
        List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
//        OrderEntity orderEntity =
//                this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        //对象一旦创建就不为空 size才能判断是否为null
        if (currentUserCartItems!=null&&currentUserCartItems.size()>0){
            List<OrderItemEntity> itemEntities = currentUserCartItems.stream().map(cartItem -> {
                OrderItemEntity itemEntity = buildOrderItem(cartItem);
                itemEntity.setOrderSn(orderSn);
//                itemEntity.setOrderId(orderEntity.getId());
                return itemEntity;
            }).collect(Collectors.toList());
            return itemEntities;
        }
        return null;
    }

    /**
     * 构建某一个订单项内容
     * @param cartItem
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo cartItem) {
        OrderItemEntity itemEntity = new OrderItemEntity();
        //1.订单信息;订单号
        //2.商品的SPU信息
        Long skuId = cartItem.getSkuId();
        R r = productFeignService.getSpuInfoBySkuId(skuId);
        SpuInfoVo spuInfo = r.getData(new TypeReference<SpuInfoVo>() {
        });

        itemEntity.setSpuId(spuInfo.getId());
        itemEntity.setSpuBrand(spuInfo.getBrandId().toString());
        itemEntity.setSpuName(spuInfo.getSpuName());
        itemEntity.setCategoryId(spuInfo.getCatalogId());

        //3.商品的sku信息
        itemEntity.setSkuId(cartItem.getSkuId());
        itemEntity.setSkuName(cartItem.getTitle());
        itemEntity.setSkuPic(cartItem.getImage());
        itemEntity.setSkuPrice(cartItem.getPrice());
        // 把一个集合按照指定的字符串进行分割得到一个字符串
        // 属性list生成一个string
        String skuAttr = StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";");
        itemEntity.setSkuAttrsVals(skuAttr);
        itemEntity.setSkuQuantity(cartItem.getCount());

        //4 积分信息
        itemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
        itemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());

        // 5.订单项的价格信息 优惠金额
        itemEntity.setPromotionAmount(new BigDecimal("0.0")); // 促销打折
        itemEntity.setCouponAmount(new BigDecimal("0.0")); // 优惠券
        itemEntity.setIntegrationAmount(new BigDecimal("0.0")); // 积分

        // 当前订单项的原价  itemEntity.getSkuQuantity().toString()把一个整数转化为一个字符串，称为bigdecimal类型
        BigDecimal orign = itemEntity.getSkuPrice().multiply(new BigDecimal(itemEntity.getSkuQuantity().toString()));
        // 减去各种优惠的价格 RealAmount
        BigDecimal subtract =
                orign.subtract(itemEntity.getCouponAmount()) // 优惠券逻辑没有写，应该去coupon服务查用户的sku优惠券
                .subtract(itemEntity.getPromotionAmount()) // 官方促销
                .subtract(itemEntity.getIntegrationAmount()); // 京豆/积分
        itemEntity.setRealAmount(subtract);

        return itemEntity;
    }

}
